/*
 * Copyright (C) 2006 Roman Krashanitsa
 *
 * @author Roman Krashanitsa
 *
 * This file is part of paparazzi.
 *
 * paparazzi is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * paparazzi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with paparazzi; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA. 
 *
 */



package pprz.components;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Button;
import java.awt.CheckboxMenuItem;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Label;
import java.awt.MenuItem;
import java.awt.Paint;
import java.awt.Point;
import java.awt.PopupMenu;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.TextField;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.image.VolatileImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;

import javax.swing.SpringLayout;

import pprz.data_components.Aircraft;
import pprz.data_components.GTile;
import pprz.data_components.GeographicConverter;
import pprz.data_components.Message;
import pprz.data_components.events.AcSelectEvent;
import pprz.data_components.events.AcSelectEventListener;
import pprz.data_components.events.DownlinkMessageEvent;
import pprz.data_components.events.DownlinkMessageEventListener;
import pprz.data_components.events.MapLocationEvent;
import pprz.data_components.events.MapLocationEventListener;
import pprz.data_components.events.MapMarkAddRemoveEvent;
import pprz.data_components.events.MapMarkAddRemoveEventListener;
import pprz.light.LightButton;
import pprz.light.LightPanel;
import pprz.light.LightSlider;

//import javax.swing.SpringLayout;


public class Map extends Container 
implements ImageObserver, ComponentListener, PropertyChangeListener,
		   MouseListener, MouseMotionListener, KeyListener,
		   DownlinkMessageEventListener, ActionListener, AcSelectEventListener,
		   MapMarkAddRemoveEventListener, MapLocationEventListener {
	
	protected static final long serialVersionUID = 1L;
	public static enum MarkType {HOME, WAYPOINT, AIRCRAFT, LINE, MARK};
	
	
	class MapToolBar extends Container
	implements MouseListener, ComponentListener
	
	{
		static final long serialVersionUID=1;
		protected LightPanel toolbar;
		protected LightButton tip;

		public MapToolBar()
		{
			FlowLayout layout=new FlowLayout();
			layout.setHgap(0);
			layout.setVgap(0);
			this.setLayout(layout);
			toolbar=new LightPanel();
			toolbar.setBackgroundPaint(new Color(0.f, 0.f, 0.f, 0.5f));
			toolbar.addComponentListener(this);
			tip=new LightButton(Toolkit.getDefaultToolkit().createImage("tip_default.png"));
			Paint def=new Color(0.f,0.f,0.f,0.f);
			tip.setBackground(new Color(0f,0f,0f,0f));
			//tip.setPaint(new Color(0f,0f,0f,0.f),new Color(0.f,0.f,0.f,0.f));
			tip.setHMargin(0);
			tip.setVMargin(0);
			tip.setAlignmentY(0.5f);
			tip.setMouseOverIcon(Toolkit.getDefaultToolkit().createImage("tip_mouseover.png"));
			tip.addComponentListener(this);
			tip.addMouseListener(this);
			GridBagConstraints c=new GridBagConstraints();
			c.fill=GridBagConstraints.VERTICAL;
			c.weightx=1.0;
			c.weighty=1.0;			
			this.add(toolbar,c);
			this.setBackground(new Color(0.f, 0.f, 0.f, 0.f));
			tip.setSize(16,16);
			tip.setPreferredSize(new Dimension(16,16));
			this.add(tip);
			LightButton b;
			
			Paint pushed=new Color(.0f,.0f,.0f,0.3f);
			b=new LightButton(Toolkit.getDefaultToolkit().createImage("Pen-32x32.png"));
			b.setMouseOverIcon(Toolkit.getDefaultToolkit().createImage("Pen-48x48.png"));
			b.setSize(64,64);
			b.setBackground(new Color(0f,0f,0f,0f));
			b.setPreferredSize(new Dimension(64,64));
			addComponent(b);
			b=new LightButton(Toolkit.getDefaultToolkit().createImage("minus_default.png"));
			b.setMouseOverIcon(Toolkit.getDefaultToolkit().createImage("minus_mouseover.png"));
			b.setDefaultPaint(def);
			b.setMousePressedPaint(pushed);
			b.setSize(24,24);
			b.setPreferredSize(new Dimension(24,24));
			addComponent(b);
			b=new LightButton(Toolkit.getDefaultToolkit().createImage("plus_default.png"));
			b.setMouseOverIcon(Toolkit.getDefaultToolkit().createImage("plus_mouseover.png"));			
			b.setDefaultPaint(def);
			b.setMousePressedPaint(pushed);
			b.setSize(24,24);
			b.setPreferredSize(new Dimension(24,24));
			addComponent(b);
			toolbar.validate();
			this.setRolled(true);
			this.setSize(layout.preferredLayoutSize(this));
			Dimension d=toolbar.getPreferredSize();
			validate();
		}

		public void addComponent(Component b)
		{
			final GridBagConstraints c=new GridBagConstraints();
			//c.fill=GridBagConstraints.VERTICAL;
			c.insets=new Insets(2,2,2,3);
			//c.anchor=GridBagConstraints.SOUTHWEST;
//			c.fill=GridBagConstraints.VERTICAL;
			c.weightx=1.0;
			c.weighty=1.0;
			toolbar.getContentPane().add(b,c); 
		}
		
		public void paint(Graphics g)
		{
//			doLayout();
			Graphics2D g2=(Graphics2D)g;
			g2.setColor(getBackground());
			g2.fillRect(0, 0, this.getWidth(), this.getHeight());
			super.paint(g);
		}
	
		@Override
		public Dimension getMinimumSize() {
			return this.getLayout()!=null?this.getLayout().minimumLayoutSize(this):super.getMinimumSize();
		}

		@Override
		public Dimension getPreferredSize() {
			return this.getLayout()!=null?this.getLayout().preferredLayoutSize(this):super.getPreferredSize();
		}	

		public void validate()
		{
			super.validate();
			toolbar.validate();
			this.setSize(this.getLayout().preferredLayoutSize(this));
			this.setPreferredSize(this.getLayout().preferredLayoutSize(this));
		}
		
		public void mouseClicked(MouseEvent arg0) {}

		public void mouseEntered(MouseEvent arg0) {}

		public void mouseExited(MouseEvent arg0) {}

		public void mousePressed(MouseEvent e) {
			if (e.getSource().equals(tip))
			{
				if (this.getX()<0) setRolled(false);
				else setRolled(true);
			}			
		}

		public void mouseReleased(MouseEvent arg0) {}
		
		public void setRolled(boolean rolled)
		{
			if (rolled)
			{
				this.setLocation(-this.getWidth()+tip.getWidth(), this.getY());
			}
			else this.setLocation(0, this.getY());
		}

		public void componentHidden(ComponentEvent arg0) {}

		public void componentMoved(ComponentEvent arg0) {}

		public void componentResized(ComponentEvent arg0) {
			this.setSize(this.getLayout().preferredLayoutSize(this));
		}

		public void componentShown(ComponentEvent arg0) {}
		
		
		
	}
	
	
	class MapTile extends Component {
		
		static final long serialVersionUID=1;
		static final float SATURATION=0.5f;
		
		protected BufferedImage tile;
		protected boolean isReady;
		
		public MapTile(int x, int y, int width, int height) {
			this.setBounds(x, y, width, height);
			tile = null;
			isReady=false;
		}

		public boolean isReady() {return this.isReady;}
		public boolean imageUpdate(Image img, int infoflags, int x, int y,
				int w, int h) {
			if ((infoflags & ImageObserver.ALLBITS) != 0) {
				System.out.println("Image Complete");
				tile = new BufferedImage(w, h,
						BufferedImage.TYPE_4BYTE_ABGR);
				Graphics2D dbg = (Graphics2D) tile.getGraphics();
				dbg.setColor(getForeground());
//				dbg.setComposite(AlphaComposite.getInstance(
//						AlphaComposite.SRC_OVER, 0.5f));
				dbg.drawImage(img, 0, 0, this);
				dbg.dispose();
				AdjustSaturation(-1.0f);
				isReady=true;
				this.repaint(100, this.getX(), this.getY(), this.getWidth(), this.getHeight());
				return false;
			} else if ((infoflags & (ImageObserver.ERROR | ImageObserver.ABORT)) == 0) {
				//System.out.println("x: " + x + " y: " + y + " width: " + w
				//		+ " height: " + h);
				//this.repaint();
			}
			return true;
		}
		
		public void AdjustSaturation(float percent)
		{
				int w = tile.getWidth();
				int h = tile.getHeight();
				//byte[] row;
				byte[] row;
				row = new byte[w*4];
				int a, r, g, b;
				Integer argb;
				float[] f = new float[3];
				for(int y = 0; y<h; y++) {
					tile.getRaster().getDataElements(0,y,w,1,row);
					for(int x = 0; x<w*4; x+=4) {
						r=row[x]&0xFF;
						g=row[x+1]&0xFF;
						b=row[x+2]&0xFF;
						a=row[x+3]&0xFF;
						Color.RGBtoHSB(r,g,b,f);
						f[1] = (f[1]+percent)%1;
						if (f[1]<0.f) f[1]=0.f;
						argb = Color.HSBtoRGB(f[0],f[1],f[2]);
						r = ((argb >> 16) & 0xff);
						g = ((argb >> 8) & 0xff);
						b = ((argb >> 0) & 0xff);
						
						row[x]=(byte)r; row[x+1]=(byte)g; row[x+2]=(byte)b; row[x+3]=(byte)a;
					}
					tile.getRaster().setDataElements(0,y,w,1,row);
				}
		}

	}
	// class for any object placed on the map
	class MapObject extends Component 
	implements MouseListener, MouseMotionListener, ActionListener 
	{
		protected static final long serialVersionUID = 1L;
				 
		protected GeneralPath shape;
		protected GeneralPath tf_shape; //transformed shape
		protected GeneralPath markee; //dragged shape
		protected Point2D.Double latlon;
		protected Point location;
		protected double altitude;
		protected Point2D.Double scale;
		protected MarkType type;
		protected Map map;
		protected Frame popup;
		protected boolean hold; //don't repaint on message
		
		public MapObject()
		{allocate();initialize();}
		
		public MapObject(GeneralPath s)
		{allocate(); initialize(); setShape(s);}

		public MapObject(GeneralPath s, Point2D.Double latlon)
		{allocate(); this.latlon=latlon; initialize(); setShape(s);}

		protected void allocate()
		{
			shape=new GeneralPath();
			tf_shape=new GeneralPath();
			latlon=new Point2D.Double(); //lat/long location
			location=new Point(); // world pixel location
			scale=new Point2D.Double(1,1);
			popup=new Frame();

		}
		
		protected void initialize()
		{
			type=MarkType.MARK;
			this.addMouseListener(this);
			this.addMouseMotionListener(this);
			popup.setVisible(false);
			popup.setAlwaysOnTop(true);
			popup.setResizable(false);
			popup.setTitle("waypoint properties");
			Container content=popup;
			Button bSET=new Button("Set");
			bSET.addActionListener(this);
			Button bCANCEL=new Button("Cancel");
			bCANCEL.addActionListener(this);
			Label l_latlon=new Label("Decimal latitude, longitude"); 
			TextField t_latlon=new TextField(30);
			Label l_alt=new Label("altitude:"); 
			TextField t_alt=new TextField(String.valueOf(altitude),15);
			
			SpringLayout layout = new SpringLayout();
			layout.putConstraint(SpringLayout.WEST, l_latlon,5,SpringLayout.WEST, content);
			layout.putConstraint(SpringLayout.NORTH, l_latlon,5,SpringLayout.NORTH, content);
			layout.putConstraint(SpringLayout.WEST, t_latlon,5,SpringLayout.WEST, content);
			layout.putConstraint(SpringLayout.NORTH, t_latlon,5,SpringLayout.SOUTH, l_latlon);
			layout.putConstraint(SpringLayout.WEST, l_alt,5,SpringLayout.WEST, content);
			layout.putConstraint(SpringLayout.NORTH, l_alt,5,SpringLayout.SOUTH, t_latlon);
			layout.putConstraint(SpringLayout.WEST, t_alt,5,SpringLayout.EAST, l_alt);
			layout.putConstraint(SpringLayout.NORTH, t_alt,5,SpringLayout.SOUTH, t_latlon);
			layout.putConstraint(SpringLayout.SOUTH, bSET,-5,SpringLayout.SOUTH, content);
			layout.putConstraint(SpringLayout.SOUTH, bCANCEL,-5,SpringLayout.SOUTH, content);
			layout.putConstraint(SpringLayout.WEST, bSET,5,SpringLayout.WEST, content);
			layout.putConstraint(SpringLayout.EAST, bCANCEL,-5,SpringLayout.EAST, content);
			
	        content.setLayout(layout);
			content.add(bSET); content.add(bCANCEL);
			content.add(l_latlon); content.add(t_latlon);
			content.add(l_alt); content.add(t_alt);
//			content.setBackground(new Color(0xDDEEEE));
			popup.setMinimumSize(new Dimension(220,180));
			popup.setPreferredSize(new Dimension(220,180));
			popup.setSize(new Dimension(200,120));
			popup.setLocation(0,0);
			popup.pack();
			popup.setLocationRelativeTo(this);
		}
		
		public void setShape(GeneralPath s)
		{
			shape=s;
			Rectangle r=shape.getBounds();
			this.setSize(new Dimension(r.width,r.height));
		}
		
		public void setLatLon(double lat, double lon) //position in lat/long
		{
			latlon.setLocation(lat, lon);
			((TextField)(popup.getComponent(3))).setText(String.format("%3.6f, %3.6f",lat,lon));
			repaint();
		}
		
		public void setLatLon(Point2D.Double latlon) //position in lat/long
		{
			latlon.setLocation(latlon);
			((TextField)(popup.getComponent(3))).setText(String.format("%3.6f, %3.6f",latlon.y,latlon.x));
			
			repaint();
		}

		public void setAltitude(double alt)
		{
			altitude=alt;
			((TextField)(popup.getComponent(5))).setText(String.valueOf(alt));
			repaint();
		}
		
		public void setLocation(int x, int y) //position on screen
		{
			location.setLocation(x, y);
			setLatLon(GTile.pix2LL(x, y));
		}
		
		public void setLocation(Point loc) //position on screen
		{
			location.setLocation(loc);
			setLatLon(GTile.pix2LL(loc.x, loc.y));
		}	
		
		public void setScale(double x, double y)
		{
			scale.setLocation(x, y);			
		}
		
		public void setScale(Point2D.Double s)
		{
			scale.setLocation(s);			
		}
		
		public void setType(MarkType t)
		{
			type=t; 
			if (type==Map.MarkType.LINE) 
			this.removeMouseListener(this);
		}

		public void update()
		{
			updateSize();
			updateLocation();
		}
		
		public void updateLocation()
		{
			Point p=map.map2screen(location);
			super.setLocation(p.x-this.getWidth()/2,p.y-this.getHeight()/2);
		}
		
		public void updateSize()
		{
			AffineTransform t=new AffineTransform();
			t.setToIdentity();
			t.translate(scale.x*map.scale/256.f/2, scale.y*map.scale/256.f/2);
			t.scale(scale.x*map.scale/256.f,scale.y*map.scale/256.f);
			
			tf_shape.reset(); tf_shape.append(shape.createTransformedShape(t), false);
			Dimension d=tf_shape.getBounds().getSize();
			super.setSize((int)(d.getWidth()+1),(int)(d.getHeight()+1));
		}
		
		@Override
		public boolean isLightweight() {
			return true;
		}

		@Override
		public boolean isOpaque() {
			return false;
		}

		public GeneralPath getShape()
		{
			return shape;
		}
		
		public void setParentMap(Map m)
		{
			map=m;
			//map.add(popup);			
		}
		
		
		
		public void actionPerformed(ActionEvent e) {
			
			if (e.getActionCommand().equals("Set"))
			{
				String s=((TextField)(popup.getComponent(3))).getText();
				String[] ss=s.split(",");
				float newLat=Float.valueOf(ss[0]);
				float newLong=Float.valueOf(ss[1]);
				float newAlt=Float.valueOf(((TextField)(popup.getComponent(5))).getText());
				System.out.println("OK pressed");
				System.out.println("Sending "+"Long"+"="+newLong+" to "+String.valueOf(map.acInfo.get(map.current_ac).get_id()));
				/*					if (parent.acInfo.get(parent.ac).uplink()!=null) //airplane has uplink
									{
										MsgProtocol p=parent.acInfo.get(parent.ac).uplink()._dec;
										Device d=parent.acInfo.get(parent.ac).uplink()._dev;
										//send new value
										if (p.getClass()==PprzMsgProtocol.class && d instanceof UplinkDevice) {
											((PprzMsgProtocol)p).encode_DL_SETTING((UplinkDevice)d,parent.ac, index-1, cur_val);
										}
									}
				*/					
			} else if (e.getActionCommand().equals("Cancel"))
				{System.out.println("Cancel pressed");}
			//hide dialog
			popup.setVisible(false);

		}

		public Point2D.Double getLatLon()
		{return latlon;}
				
		@Override
		public void paint(Graphics g) {
			super.paint(g);
			Graphics2D g2=(Graphics2D)g;
			g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
			g2.setStroke(new BasicStroke(3));
			g2.setColor(Color.green);
			//g2.drawRect(0, 0, this.getWidth()-1, this.getHeight()-1);
		
			if (type==MarkType.WAYPOINT)
			{
				g2.setColor(new Color(0xAA8800));
				g2.fill(tf_shape);
			}
			else if (type==MarkType.LINE)
			{
				g2.setColor(new Color(0xAA8800));
				g2.draw(tf_shape);
			}
			else if (type==MarkType.MARK)
			{
				g2.setColor(new Color(0xAA8800));
				g2.draw(tf_shape);
			}
			else if (type==MarkType.HOME)
			{
				g2.setColor(new Color(0xAA8800));
				g2.fill(tf_shape);
			}
				
			
		}

		private void ShowMarker(int x, int y)
		{
			Graphics2D g=(Graphics2D)map.getGraphics();
			g.setXORMode(Color.white);
			g.setStroke(new BasicStroke(5, BasicStroke.CAP_BUTT, 
					BasicStroke.JOIN_ROUND, 0.2f));
			
			if (markee==null) markee=new GeneralPath();
			else {
				g.draw(markee);
			}
			Line2D.Float curve=new Line2D.Float(this.getX()+this.getWidth()/2, this.getY()+this.getHeight()/2, x, y);
			Ellipse2D.Float circle=new Ellipse2D.Float(x-5,y-5,10,10);
			markee.reset();
			markee.append(curve, false);
			markee.append(circle, false);
			g.draw(markee);
		}
		
		
		public void mouseDragged(MouseEvent arg0) {
			map.frozen=true;
			ShowMarker(arg0.getX()+this.getX(), arg0.getY()+this.getY());
		}

		public void mouseMoved(MouseEvent arg0) {}

		public void mouseClicked(MouseEvent e) {
			checkPopup(e);
		}

		public void mouseEntered(MouseEvent e) {}

		public void mouseExited(MouseEvent e) {}

		public void mousePressed(MouseEvent e) {
			checkPopup(e);
		}

		public void mouseReleased(MouseEvent e) {
			if (map.frozen)
			{
				map.frozen=false;
				Point p=map.screen2map(this.getX()+e.getX(), this.getY()+e.getY());
				Point2D.Double ll=GTile.pix2LL(p.x, p.y);
				
				((TextField)this.popup.getComponent(3)).setText(String.format("%11.6f, %11.6f", ll.y,ll.x));
            	popup.pack();
            	popup.setLocation(this.getX()+e.getX(), this.getY()+e.getY());
                popup.setVisible(true);
			}
			if (markee!=null) markee.reset();
			checkPopup(e);
		}
		
        protected void checkPopup(MouseEvent e) {
            if (e.isPopupTrigger(  )) {
            	popup.setLocation(this.getX(), this.getY());
//            	popup.setLocationRelativeTo(this);
                popup.setVisible(true);
            }
        }
		
		
	}
	
	
	
	
	//image variables	
	protected VolatileImage dbImage; 
	//protected BufferedImage layer1;
	protected Image[][] grid;  
	protected HashMap<Integer, BufferedImage> airplaneLayers;  //  @jve:decl-index=0:
	protected BufferedImage mapMarksLayer;
	protected HashMap<Integer,MapObject> mapMarks;  //  @jve:decl-index=0:
	protected HashMap<Point, MapTile> tiles;  //  @jve:decl-index=0:

	BufferedImage back;  //  @jve:decl-index=0:
	//tile size and scale
	protected int scale=256;
	//base of the map
	protected double x;
	protected double y;
//	protected double z;
	//map width and height
	protected int width;
	protected int height;
	//shift of the backBuffer image
	protected int origX;
	protected int origY;
	//tile system dimensions
	protected Dimension backGridSize;  //  @jve:decl-index=0:
	protected Dimension gridSize;  //  @jve:decl-index=0:
	// built-in controls
	protected LightSlider zoom;
	protected PopupMenu menu;  //  @jve:decl-index=0:
	// keyboard and mouse variables
	protected boolean isPanning;
	protected boolean frozen; // don't redraw on message
	protected Point panStart;  //  @jve:decl-index=0:
	//map objects
	protected HashMap<Integer,Aircraft> acInfo=null;  //there is only one
	
	protected int current_ac;
	
	protected static int[] msgid={8,10,11,12,16,20,21,22,27,29,32};
	protected HashSet<Integer> msgSet;  //  @jve:decl-index=0:
	
	protected boolean isLayersOn=true;
	protected boolean isMapOn=true;
	protected boolean isObjectsOn=true;
	
	protected MapToolBar toolbar;
	
	protected int trajX[];
	protected int trajY[];
	protected int trajZ[];
	protected double shapeArray[]={	
			137.94208, 567.68003,
			141.17159, 567.68003,
			142.05260, 571.88551,
			143.96683, 568.19480,
			146.89191, 565.84120,
			153.53745, 564.41112,
			160.30630, 565.68444,
			165.20825, 568.66479,
			166.25339, 572.35493,
			161.84268, 577.52504,
			154.70872, 582.72661,
			148.06006, 586.75238,
			145.10353, 588.39506,
			151.33975, 621.91106,
			151.33691, 628.45597,
			212.06920, 628.45597,
			217.07036, 629.85770,
			221.16557, 633.67427,
			223.93276, 639.32315,
			224.94898, 646.22126,
			224.17880, 652.00564,
			221.82151, 657.77811,
			217.80794, 662.21348,
			212.06920, 663.98655,
			151.33521, 663.98655,
			151.33975, 684.55701,
			144.64460, 684.55701,
			144.14088, 686.24447,
			142.76920, 689.00740,
			140.60041, 691.58806,
			137.66995, 692.72787,
			134.73921, 691.58806,
			132.57043, 689.00740,
			131.19874, 686.24447,
			130.69502, 684.55701,
			124.62576, 684.55701,
			124.63030, 663.98655,
			63.89631, 663.98655,
			58.15757, 662.21348,
			54.14400, 657.77811,
			51.78671, 652.00564,
			51.01654, 646.22126,
			52.03276, 639.32315,
			54.79994, 633.67427,
			58.89515, 629.85770,
			63.89631, 628.45597,
			124.62860, 628.45597,
			124.62576, 621.91106,
			130.86198, 588.39506,
			127.90545, 586.75238,
			121.25679, 582.72661,
			114.12283, 577.52504,
			109.71213, 572.35493,
			110.75726, 568.66479,
			115.65921, 565.68444,
			122.42806, 564.41112,
			129.07361, 565.84120,
			131.99868, 568.19480,
			133.91291, 571.88551,
			134.79392, 567.68003,
			137.94208, 567.68003
	};
	protected static GeneralPath airplaneShape;  //  @jve:decl-index=0:
	protected GeneralPath airplaneShape0;  //  @jve:decl-index=0:
	//temporary variables to avoid junk collection
	Point p;

	public Map(HashMap<Integer,Aircraft> acInfo) {
		super();
		allocate();		
		initialize();
		this.acInfo=acInfo;
	}
	
	public Map(double x, double y, double z, HashMap<Integer,Aircraft> acInfo)
	{
		super();
		allocate();
		setLoc(x,y,z);
		initialize();
		this.acInfo=acInfo;
	}

	protected void allocate()
	{
		p=new Point();
		panStart=new Point();
		gridSize= new Dimension();
		backGridSize= new Dimension();
		grid=new Image[1][1];
		tiles=new HashMap<Point,MapTile>();
		back=new BufferedImage(scale,scale,BufferedImage.TYPE_3BYTE_BGR );
		mapMarks=new HashMap<Integer,MapObject>();
		msgSet=new HashSet<Integer>(11);
		airplaneLayers=new HashMap<Integer, BufferedImage>();
		trajX=new int[Aircraft.traj_len];
		trajY=new int[Aircraft.traj_len];
		trajZ=new int[Aircraft.traj_len];
		airplaneShape=new GeneralPath();
		zoom=new LightSlider(0.5);
		menu=new PopupMenu();
		toolbar=new MapToolBar();
	}

	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	protected void initialize() {
		//this.setLayout(new BorderLayout());
		//initial size
		height=200;
		width=320;
		this.frozen=false;
		//initial tile grid size
		gridSize.setSize((int)(width/scale)+2,(int)(height/scale)+2);
		backGridSize.setSize(gridSize);
		//make background image
		Graphics2D g=back.createGraphics();
		g.setColor(new Color(0.7f,0.7f,0.7f));
		g.fillRect(0, 0, back.getWidth(), back.getHeight());
		g.setColor(Color.gray); g.setStroke(new BasicStroke(2));
		g.drawLine(0, 0, back.getWidth(), 0);
		g.drawLine(0, back.getHeight(), back.getWidth(), back.getHeight());
		g.drawLine(back.getWidth(), 0, back.getWidth(), back.getHeight());
		g.drawLine(0, 0, 0, back.getHeight());
		g.dispose();
		// build airplane shape
		airplaneShape.moveTo((float)shapeArray[0],(float)shapeArray[1]);
		for (int i=2; i<shapeArray.length;i+=2)
			airplaneShape.lineTo((float)shapeArray[i],(float)shapeArray[i+1]);
		airplaneShape.closePath();
		Rectangle b=airplaneShape.getBounds();
		airplaneShape.transform(
				AffineTransform.getTranslateInstance(-b.getX()-b.width/2, 
													 -b.getY()-b.height/2
				)				);
		airplaneShape.transform(
				AffineTransform.getScaleInstance(0.2,0.2));
		airplaneShape0=airplaneShape;
		//populate set of allowed message id
		for (int i=0; i<msgid.length;i++) msgSet.add(msgid[i]);
		//set dimensions
		this.setMinimumSize(new Dimension(150, 150));
		this.setPreferredSize(new Dimension(150, 150));
		this.setSize(new Dimension(150, 150));
		//add listeners
		this.addComponentListener(this);
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		this.addKeyListener(this);
		//set component dimensions
		zoom.setSize(40,80);
		zoom.setLocation(width-20, 0);
		zoom.addPropertyChangeListener(this);
		setScale(zoom.getValue());
		this.add(zoom);
		menu.add("show tile");
		menu.add("center map");
		menu.add(new CheckboxMenuItem("fill tiles"));
		menu.add("redraw map");
		this.add(menu);
		menu.addActionListener(this);
		//toolbar.add(new PopupMenu());
		toolbar.setSize(toolbar.getPreferredSize());
		//toolbar.setSize(new Dimension(100,50));
		//toolbar.setLocation(-(toolbar.getWidth()-20),height-toolbar.getHeight());
		toolbar.setLocation(0,this.getHeight()-toolbar.getHeight());
		toolbar.addMouseListener(this);
		toolbar.addComponentListener(this);
		this.add(toolbar);
		//this.setComponentZOrder(toolbar, this.getComponentCount()-1);
		//this.validate();
		
		//finish
		centerMap();
		//reTile();
	}
	
	//internal convenience function
	protected void setLoc(double x, double y, double z)
	{
		this.x=x;
		this.y=y;
//		this.z=z;
	}

	//internal convenience function
	protected void setLoc(Point2D.Double p)
	{
		this.x=p.x;
		this.y=p.y;
	}
	

	//bg plane pixels to world pixels	
	public Point bg2map(int px,int py)
	{
		//calculate map focus point in back buffer coordinates
		Point pw=GTile.UTM2Pix(y, x, 0); //pixel world coordinates
		//coordinates of upper-left corner of the backBuffer in world pixels
		int x=(pw.x/256-gridSize.width/2)*256;
		int y=(pw.y/256-gridSize.height/2)*256;
		//add scaled origin and p and obtain coordinates of point in world pixels
		return new Point(x+px*256/scale,y+py*256/scale); 
	}
	//world pixels to bg plane pixels
	public Point map2bg(int px, int py)
	{
		//calculate map focus point in back buffer coordinates
		Point pw=GTile.UTM2Pix(y, x, 0); //pixel world coordinates
		//coordinates of upper-left corner of the backBuffer in world pixels
		int x=(pw.x/256-gridSize.width/2)*256;
		int y=(pw.y/256-gridSize.height/2)*256;
		//subtract scaled screen origin and backBuffer origin from p 
		//and obtain coordinates of point in screen pixels
		return new Point((px-x)*scale/256,(py-y)*scale/256); 
	}
	//screen pixels to world pixels
	public Point screen2map(int px,int py)
	{
		//calculate map focus point in back buffer coordinates
		Point pw=GTile.UTM2Pix(y, x, 0); //pixel world coordinates
		//coordinates of upper-left corner of the backBuffer in world pixels
		int x=(pw.x/256-gridSize.width/2)*256;
		int y=(pw.y/256-gridSize.height/2)*256;
		//add scaled origin and p and obtain coordinates of point in world pixels
		return new Point(x+(-origX+px)*256/scale,y+(-origY+py)*256/scale); 
	}
	
	//world pixels to screen pixels
	public Point map2screen(int px, int py)
	{
		//calculate map focus point in back buffer coordinates
		Point pw=GTile.UTM2Pix(y, x, 0); //pixel world coordinates
		//coordinates of upper-left corner of the backBuffer in world pixels
		int x=(pw.x/256-gridSize.width/2)*256;
		int y=(pw.y/256-gridSize.height/2)*256;
		//subtract scaled screen origin and backBuffer origin from p 
		//and obtain coordinates of point in screen pixels
		return new Point((px-x+origX)*scale/256,(py-y+origY)*scale/256); 
	}
	
	public Point map2screen(Point p)
	{
		//calculate map focus point in back buffer coordinates
		Point pw=GTile.UTM2Pix(y, x, 0); //pixel world coordinates
		//coordinates of upper-left corner of the backBuffer in world pixels
		int x=(pw.x/256-gridSize.width/2)*256;
		int y=(pw.y/256-gridSize.height/2)*256;
		//subtract scaled screen origin and backBuffer origin from p 
		//and obtain coordinates of point in screen pixels
		return new Point((p.x-x+origX)*scale/256,(p.y-y+origY)*scale/256); 
	}
	
	
	public void centerMap()
	{
		//calculate pixel on the tile where location is
		Point pw=GTile.UTM2Pix(y, x, 0);
		origX=-((gridSize.width/2)*scale+pw.x%256*scale/256-width/2);
		origY=-((gridSize.height/2)*scale+pw.y%256*scale/256-height/2);

	}
	
	protected boolean isMapObject(int x, int y)
	{
		if (this.getComponentAt(x, y)!=null) return true;
		return false;
	}

	public Image getTile(int x, int y)
	{
				Point key=new Point(x,y);
				
				BufferedImage im=back;
				if (!tiles.containsKey(key)) 
				{
					if (((CheckboxMenuItem)menu.getItem(2)).getState())
					{
						MapTile mapTile=new MapTile(x,y,256*this.scale,256*this.scale);
						GTile.get(x,y,0,mapTile);
						tiles.put(key, mapTile);
						im=tiles.get(key).tile;
					}
				} 
				else  im=tiles.get(key).tile;
				
				
				if (im==null) return back;
				
				return im;
	}
	
	public void setScale(double pos)
	{
		scale=(int)(2.0*128.0*pos)+128;
		airplaneShape0=(GeneralPath)airplaneShape.createTransformedShape(AffineTransform.getScaleInstance(scale/256.,scale/256.));
		Component f[]=this.getComponents();
		for (int i=0;i<f.length;i++) {
			if (f[i].getClass().equals(MapObject.class))
			{
				if (((MapObject)f[i]).type==MarkType.LINE)
				{
					f[i].setSize(f[i].getHeight()*scale, f[i].getWidth()*scale);
				}	
			}
		}
	}
	
	@Override
	public void validate() {
		super.validate();		
//		Point pm=screen2map(width/2,height/2);
//		Point2D.Double p=GTile.pix2LL(pm.x,pm.y);
//		GeographicConverter.UTM utm=GeographicConverter.LLtoUTM(23, p.y, p.y);
//		setLoc(utm.easting,utm.northing,0);		
//		gridSize.width=(int)((width-origX)/scale)+2;
//		gridSize.height=(int)((height-origY)/scale)+2;
		gridSize.width=(int)(width/scale)+2;
		gridSize.height=(int)(height/scale)+2;
//		centerMap();	
		//reTile();
		zoom.setLocation(this.getWidth()-zoom.getWidth(),0);
		toolbar.setLocation(toolbar.getX(), this.getHeight()-toolbar.getHeight());				
	}

	public void paint(Graphics g) {
			draw(g);
			super.paint(g);
	}
	
	protected void draw(Graphics g)
	{
		if (isMapOn) g.drawImage (dbImage,origX,origY, this);
		if (isLayersOn) { 
			Collection<BufferedImage> c=airplaneLayers.values();
			for (BufferedImage layer : c) {
				g.drawImage (layer,origX,origY, this);
			}
		}
	}

	@Override
	public boolean imageUpdate(Image img, int infoflags,
            					int x, int y, int w, int h) {
	     if ((infoflags & ImageObserver.ALLBITS) != 0) {
	    	System.out.println("WRONG Image Complete");
			BufferedImage im2=new BufferedImage(w,h,BufferedImage.TYPE_4BYTE_ABGR);
	    	Graphics2D dbg=(Graphics2D)im2.getGraphics();
	    	dbg.setColor (getForeground());
			dbg.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f));
			dbg.drawImage(img, 0, 0, this);
			dbg.dispose();
			img=im2;
//1	        repaint(100);
	        return false;
	     }
	     else if ((infoflags & (ImageObserver.ERROR | ImageObserver.ABORT))==0)
	     {
	        //System.out.println("x: " + x + " y: " + y + " width: " + w+ " height: " + h);
//	        this.repaint();
	     }
	     return true;
	}
	
	protected void drawBack()
	{
//		 initialize buffer
		Graphics dbg;
		if (dbImage == null || 
			dbImage.getWidth()<gridSize.width*scale || 
			dbImage.getHeight()<gridSize.height*scale); 
		{
			dbImage =  getGraphicsConfiguration().createCompatibleVolatileImage(gridSize.width*scale, gridSize.height*scale, 1);
		} 
		if (dbImage!=null)
		{
			dbg = dbImage.getGraphics (); 
			//map center in global map pixel coordinates
			Point pp=GTile.UTM2Pix(y, x, 0);
			Point p=GTile.getTileLoc(pp.x, pp.y, 0);
			//calculate 
			if (grid!=null)
			if (grid.length<gridSize.width || grid[0].length<gridSize.height)
				grid=new Image[gridSize.width][gridSize.height];
			//Fill tile matrix
			int n=gridSize.width;
			int m=gridSize.height;
			for (int i=0; i<n; i++)
				for (int j=0; j<m; j++)
				{
					dbg.drawImage (getTile(p.x+(i-n/2),p.y+(j-m/2)), i*scale,j*scale, scale, scale, this);					
				}				
/*			
			if (grid!=null)
			{
				int N=grid.length;
				int M=grid[0].length;
				for (int i=0; i<N; i++)
					for (int j=0; j<M; j++)
						dbg.drawImage (grid[i][j], i*scale,j*scale, scale, scale, this);
			}
*/
			dbg.dispose();
			
		}
	}

	protected void cleanLayers()
	{
		Collection<BufferedImage> c=airplaneLayers.values();
		System.out.println(c.size());
		for (BufferedImage layer : c) {
			Graphics2D g=layer.createGraphics();
			g.setBackground(new Color(1,1,1,1));
			g.clearRect(0, 0, layer.getWidth(), layer.getHeight());
			g.dispose();
		}
	}
	
	protected void drawAcLayers()
	{
		Collection<Integer> c=airplaneLayers.keySet();;
		for (Integer id : c) {
			if (acInfo.containsKey(id))
			drawLayer(acInfo.get(id));
		}		
	}
	
	protected void drawMapMarksLayer()
	{
//		 initialize buffer
		Graphics dbg;
		Graphics2D dbg2;
		
		
		
		if (mapMarksLayer==null)
		{
			mapMarksLayer= new BufferedImage (gridSize.width*scale, gridSize.height*scale,BufferedImage.TYPE_4BYTE_ABGR_PRE);
		}
		
		else if (mapMarksLayer.getWidth()<gridSize.width*scale || 
				 mapMarksLayer.getHeight()<gridSize.height*scale )
		{
			mapMarksLayer= new BufferedImage (gridSize.width*scale, gridSize.height*scale,BufferedImage.TYPE_4BYTE_ABGR_PRE);
		}
//		System.out.println("Width:"+mapMarksLayer.getWidth()+":Height:"+mapMarksLayer.getHeight());
//		System.out.println("origX:"+origX+":origY:"+origY);
//		System.out.println(":width:"+width+"height:"+height);
		dbg = mapMarksLayer.getGraphics ();
		dbg2=(Graphics2D)dbg;
		dbg2.setRenderingHint (RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		dbg2.setBackground(new Color(0,0,0,0));
		dbg2.clearRect(0, 0, mapMarksLayer.getWidth(),mapMarksLayer.getHeight());
		dbg2.setColor(Color.green);
		//draw elements in background
		Point center_tile=GTile.getTileLoc(x, y, 0);
		AffineTransform g=new AffineTransform();
		g.setToIdentity();
		g.scale(scale/256.0,scale/256.0);
		g.translate(-(center_tile.x-(gridSize.width/2))*256,
					-(center_tile.y-(gridSize.height/2))*256);
		
		GeneralPath ss=new GeneralPath();
		Collection<MapObject> c=mapMarks.values();
		System.out.println("There are "+c.size()+"marks on the layer");
		for (MapObject s : c)
		{
			//transform into bg plane coordinates
			ss.reset();
			ss.append(s.shape, false);
			ss.transform(g);	
			if (s.getClass().isAssignableFrom(Area.class) )
			{
				dbg2.fill(ss);
			}
			else dbg2.draw(ss);
		}
		dbg2.dispose();
		dbg.dispose();		
	}
	
	protected void drawLayer(Aircraft ac)
	{
		if (ac==null) return;
//		 initialize buffer
		Graphics dbg;
		Graphics2D dbg2;
		int id=ac.get_id();
		//check if airpplane map layer exists, if not - add it
		if (!airplaneLayers.containsKey(id))
		{
			airplaneLayers.put(id, new BufferedImage ( gridSize.width*scale, 
															gridSize.height*scale,
															BufferedImage.TYPE_4BYTE_ABGR_PRE));			
		}
		
		BufferedImage layer1=airplaneLayers.get(id);
		
		if (layer1.getWidth()<gridSize.width*scale || 
			layer1.getHeight()<gridSize.height*scale )
		{
			layer1 = new BufferedImage (gridSize.width*scale, gridSize.height*scale,BufferedImage.TYPE_4BYTE_ABGR_PRE);
			airplaneLayers.put(id, layer1);
			
		}
		dbg = layer1.getGraphics ();
		dbg2=(Graphics2D)dbg;
		dbg2.setRenderingHint (RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		dbg2.setBackground(new Color(0,0,0,0));
		dbg2.clearRect(0, 0, layer1.getWidth(),layer1.getHeight());
		//draw elements in background
    	dbg2.setColor(new Color(ac.get_color()));
    	if (id==current_ac)
    		dbg2.setStroke(new BasicStroke(3));
//    	System.out.println("ac="+id+" cur="+current_ac);
		int start=ac.traj_start;
		int cur=start;
		for (int i=0; i<ac.traj_n-1;i++)
		{
			//Tile.UTM2LL(a.get_gps_utm_north(),a.get_gps_utm_east());
			p=GTile.LL2Pix(ac.traj[cur].y, ac.traj[cur].x, 0);
			p=this.map2bg(p.x,p.y);
			trajX[i]=p.x;
			trajY[i]=p.y;
			trajZ[i]=(int)ac.traj[cur].z;
			cur=(cur==Aircraft.traj_len-1)?0:cur+1; 
		}		    	
		cur=(cur==0)?Aircraft.traj_len-1:cur-1;
		if (ac.traj_n-1>0)
		{
			dbg2.drawPolyline(trajX, trajY, ac.traj_n-1);
			AffineTransform t=dbg2.getTransform();
			t.translate(trajX[ac.traj_n-2], trajY[ac.traj_n-2]);
			//System.out.println(ac.get_gps_course()/100);
			t.rotate(Math.toRadians(ac.get_yaw()+180));
			dbg2.setColor(new Color(ac.get_color()).brighter().brighter());
			Shape s=airplaneShape0.createTransformedShape(t);
			dbg2.fill(s);
			dbg2.setStroke(new BasicStroke(1));
			dbg2.setColor(new Color(ac.get_color()).darker().darker());
			dbg2.draw(s);
		}
		dbg2.dispose();
		dbg.dispose();
	}

	
	public void componentHidden(ComponentEvent e) {}
	public void componentMoved(ComponentEvent e) {}

	public void componentResized(ComponentEvent e) {
		System.out.println("public void componentResized(ComponentEvent e) {");
		width=this.getWidth();
		height=this.getHeight();
//		zoom.setLocation(this.getWidth()-zoom.getWidth(),0);
//		toolbar.setLocation(toolbar.getX(), this.getHeight()-toolbar.getHeight());
		validate();
		drawAll();
		repaint(100);
	}

	public void componentShown(ComponentEvent e) {}

	public void mouseClicked(MouseEvent arg0) {}

	public void mouseEntered(MouseEvent arg0) {}

	public void mouseExited(MouseEvent arg0) {}

	public void mousePressed(MouseEvent e) {
		System.out.println("Mouse pressed");
		if (!e.getSource().equals(this)) return;
		
		if (e.getButton()==MouseEvent.BUTTON1 )
		{
			isPanning=true;
			isMapOn=true;
			isLayersOn=false;
			setCursor(new Cursor(Cursor.MOVE_CURSOR));
			panStart.setLocation(e.getX(),e.getY());
		}
		else if (e.getButton()==MouseEvent.BUTTON3)
		{
	        if (e.isPopupTrigger()) menu.show(e.getComponent(),e.getX(), e.getY());
		}
	}

	public void mouseReleased(MouseEvent e) {
		if (!e.getSource().equals(this)) return;
		if (e.getButton()==MouseEvent.BUTTON1 && isPanning)
		{
			isPanning=false;
			isMapOn=true;
			isLayersOn=true;
			
			setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
			
			//shift the backBuffer image
			origX+=(e.getX()-panStart.x);
			origY+=(e.getY()-panStart.y);
			for (int i=0;i<this.getComponentCount();i++)
			{
				if (this.getComponents()[i].getClass()==MapObject.class)
				{
					MapObject o=(MapObject)this.getComponents()[i];
					o.updateLocation();
				}
			}
			validate();
			drawAll();
			repaint(100);
		}
		else if (e.getButton()==MouseEvent.BUTTON3)
		{
	        if (e.isPopupTrigger()) menu.show(e.getComponent(),e.getX(), e.getY());
		}		
	}

	@Override
	public boolean isOpaque() {
		return true;
	}

	public void mouseDragged(MouseEvent e) 
	{
		if (!e.getSource().equals(this)) return;
//		if (!(isPanning)) return;
		//shift the backBuffer image
		origX+=(e.getX()-panStart.x);
		origY+=(e.getY()-panStart.y);
//		System.out.println(origX+":"+origY);
		//re-tile if back-image is too small
/*		if (Math.abs(-origX+width)>gridSize.width*scale || 
			Math.abs(-origY+height)>gridSize.height*scale ||
			origX>0 || origY>0)
		{
			//validate();
			repaint(10);
		}
*/
		panStart.x=e.getX();
		panStart.y=e.getY();
		e.consume();
		//validate();
		repaint(100);
	}

	public void mouseMoved(MouseEvent arg0) {}

	public void keyPressed(KeyEvent arg0) {}

	public void keyReleased(KeyEvent arg0) {}

	public void keyTyped(KeyEvent arg0) {}

	public void propertyChange(PropertyChangeEvent arg0) {
		if (arg0.getPropertyName()=="pos") //map scale has changed 
		{
			System.out.println("public void propertyChange(PropertyChangeEvent arg0) ");
			setScale(zoom.getValue());
			validate();
			drawAll();
			repaint(100);
		}
	}
	
	
	
	public void acSelectEvent(AcSelectEvent ev) {
		current_ac=ev.getAc();
		System.out.println("current ac changed");
		Aircraft a=this.acInfo.get(current_ac);
		if (a!=null)
		{
			if (a.traj_n>0) this.setLoc(a.traj[a.traj_pos].y, a.traj[a.traj_pos].x, 0);
		}
		drawAcLayers();
		repaint(100);
	}

	public void mapMarkAddRemoveEvent(MapMarkAddRemoveEvent ev) {

		//System.out.println("map mark add/remove event");
		if (ev.isAdding)
		{
			this.addMapMark(ev.getID(), ev.getShape(), ev.getLoc(), ev.getSize());
		}
		else this.removeMapMark(ev.getID());
	}
	
	public void mapLocationEvent(MapLocationEvent ev) {

		System.out.println("map location event");
		this.lookAt(ev.getLoc());
	}
	
	public void messageEvent(DownlinkMessageEvent ev) {
		Message m = ev.getMessage();
		int msgid=m.id();
		int acid = m.acRec().get_id(); //get aircraft id
		if (!acInfo.containsKey(acid)) acInfo.put(acid, m.acRec());

		//process only downlink messages in msgSet from current AC
		if (m.msgClass()!=0 || !msgSet.contains(msgid) ) return;

		switch (msgid) {
		case 8:
/*			   <message name="GPS" ID="8">
			     <field name="mode"   type="uint8" unit="byte_mask"></field>
			     <field name="utm_east"  type="int32" unit="cm"></field>
			     <field name="utm_north"  type="int32" unit="cm"></field>
			     <field name="course" type="int16" unit="decideg"></field>
			     <field name="alt"    type="int32" unit="cm"></field>
			     <field name="speed"  type="uint16" unit="cm/s"></field>
			     <field name="climb"  type="int16" unit="cm/s"></field>
			     <field name="itow"  type="uint32" unit="ms"></field>
			     <field name="utm_zone"  type="uint8"></field>
			     <field name="gps_nb_err" type="uint8"></field>
			   </message>	*/
			if (!frozen) {
			   drawLayer(m.acRec());
			   repaint(100);
			}
			   break;
		case 10:
/*			   <message name="NAVIGATION" ID="10">
			     <field name="cur_block" type="uint8"></field>
			     <field name="cur_stage" type="uint8"></field>
			     <field name="pos_x" type="int16" unit="m"></field>
			     <field name="pos_y" type="int16" unit="m"></field>
			     <field name="desired_course" type="int16" unit="decideg"></field>
			     <field name="dist2_wp" type="float" format="%.1f" unit="m^2"></field>
			     <field name="dist2_home" type="float" format="%.1f" unit="m^2"></field>
			   </message>*/
			
			break;
		case 11:
/*		  <message name="PPRZ_MODE" ID="11">
		     <field name="ap_mode" type="uint8" values="MANUAL|AUTO1|AUTO2|HOME|NOGPS|FAILSAFE"/>
		     <field name="ap_gaz" type="uint8" values="MANUAL|AUTO_THROTTLE|AUTO_CLIMB|AUTO_ALT"/>
		     <field name="ap_lateral" type="uint8" values="MANUAL|ROLL_RATE|ROLL|COURSE"/>
		     <field name="ap_horizontal" type="uint8" values="WAYPOINT|ROUTE|CIRCLE"/>
		     <field name="if_calib_mode" type="uint8" values="NONE|DOWN|UP"/>
		     <field name="mcu1_status" type="uint8" values="LOST|OK|REALLY_LOST"/>	
		     <field name="lls_calib" type="uint8" values="LLS_CALIB_MODE_OFF|LLS_CALIB_MODE_ON"/>
		   </message>*/

			
			break;
		case 12:
/*		   <message name="BAT" ID="12">
			 <field name="throttle" type="int16" unit="pprz"></field>
			 <field name="voltage" type="uint8" unit="1e-1V"></field>
			 <field name="flight_time" type="uint16" unit="s"></field>
			 <field name="kill_auto_throttle" type="uint8" unit="bool"></field>
			 <field name="block_time" type="uint16" unit="s"></field>
			 <field name="stage_time" type="uint16" unit="s"></field>
			 <field name="energy" type="uint16" unit="mAh"/>
		    </message>*/			
			
			break;
		case 16:
		 /*<message name="DESIRED" ID="16">
		     <field name="roll" type="float" format="%.2f"/>
		     <field name="pitch" type="float" format="%.2f"/>
		     <field name="desired_x" type="float" format="%.0f"/>
		     <field name="desired_y" type="float" format="%.0f"/>
		     <field name="desired_altitude" type="float" format="%.0f"/>
		     <field name="desired_climb" type="float" format="%.1f"></field>
		   </message>*/

			break;
		case 20:
		 /*<message name="CAM" ID="20">
		     <field name="phi" type="int8" unit="deg"/>
		     <field name="theta" type="int8" unit="deg"/>
		     <field name="target_x" type="int16" unit="m"/>
		     <field name="target_y" type="int16" unit="m"/>
		   </message>*/

			break;
		case 21:
		/*<message name="CIRCLE" ID="21">
		     <field name="center_east" type="int16" unit="m"/>
		     <field name="center_north" type="int16" unit="m"/>
		     <field name="radius" type="int16" unit="m"/>
	     </message>*/
			break;
		case 22:
		/*<message name="SEGMENT" ID="22">
	      	<field name="segment_east_1" type="int16" unit="m"/>
	      	<field name="segment_north_1" type="int16" unit="m"/>
	      	<field name="segment_east_2" type="int16" unit="m"/>
	      	<field name="segment_north_2" type="int16" unit="m"/>
		  </message>*/

			break;
		case 27:
			/*<message name="SURVEY" ID="27">
			    <field name="east" type="float" unit="m"/>
			    <field name="north" type="float" unit="m"/>
			    <field name="west" type="float" unit="m"/>
			    <field name="south" type="float" unit="m"/>
			  </message>*/
			break;
		case 29:
			/*<message name="RANGEFINDER" id="29">
			    <field name="range" type="uint16" unit="cm"/>
			    <field name="z_dot" type="float" unit="m/s"/>
			    <field name="z_dot_sum_err" type="float" unit="m/s"/>
			    <field name="z_dot_setpoint" type="float" unit="m/s"/>
			    <field name="z_sum_err" type="float" unit="m/s"/>
			    <field name="z_setpoint" type="float" unit="m"/>
			    <field name="flying" type="uint8" unit="bool"/>
			  </message>*/
			break;
		case 32:
		   /*<message name="MARK" id="32">
			   <field name="ac_id" type="uint8"/>
			   <field name="lat" type="float" unit="deg"/>
			   <field name="long" type="float" unit="deg"/>
			 </message>*/
			break;
		default:
			return;
		}
	}	
	
	public void actionPerformed(ActionEvent arg0) {
		String s=arg0.getActionCommand();
		if (s.compareToIgnoreCase("show tile")==0)
		{
			System.out.println("executing -show tile-");
			Point key=this.getMousePosition();
			key=this.screen2map(key.x, key.y);
			key=GTile.getTileLoc(key.x,key.y,0);
			MapTile mapTile=new MapTile(key.x,key.y,256*scale,256*scale);
			GTile.get(key.x,key.y,0,mapTile);
			tiles.put(key, mapTile);
			//reTile();
			drawBack();
			repaint(100,key.x,key.y,256*scale,256*scale);
		} else if (s.compareToIgnoreCase("redraw map")==0)
		{
			System.out.println("executing -redraw map-");
			validate();
			drawAll();
			repaint(100);
		} else if (s.compareToIgnoreCase("center map")==0)
		{
			Aircraft a=acInfo.get(current_ac);
			//Point pw=Tile.LL2Pix(x, y, 0);
			lookAt(GeographicConverter.LLtoUTM(23, a.get_gps_utm_north(),a.get_gps_utm_east()));
		}
		
	}

	public void lookAt(double UTM_north, double UTM_west)
	{
		setLoc(UTM_west,UTM_north,0);
		//reTile();
		centerMap();
		drawAll();
		repaint(100);
	}
	
	public void lookAt(GeographicConverter.UTM loc)
	{
		setLoc(loc.easting,loc.northing,0);
		//reTile();
		centerMap();
		drawAll();
		repaint(100);
	}
	
	public void addMapMark(int key, Shape value, GeographicConverter.UTM loc, Point2D.Double size)
	{
		MapObject o=new MapObject(new GeneralPath(value));
		switch (key)
		{
		case 0:
			o.setType(MarkType.HOME);
			break;
		case 2000:
			o.setType(MarkType.LINE);
			break;
		default: //anything other than 0 or 2000
			o.setType(MarkType.WAYPOINT);
			PopupMenu p=new PopupMenu();
			p.add(new MenuItem("move"));
			o.add(p);
			break;
		}
		mapMarks.put(key, o);
		o.setParentMap(this);
		o.setLocation(GTile.UTM2Pix(loc.northing, loc.easting, 0));
		o.setScale(size);
		o.update();
		this.add(o);
	}
	
	public void removeMapMark(Integer key)
	{
		if (mapMarks.containsKey(key)) 
		{
			this.remove(mapMarks.get(key));
			mapMarks.remove(key);
		}
	}

	public void redrawMapMarks()
	{
//		System.out.println("public void redrawMapMarks()");
//		drawMapMarksLayer();
	}
	
	public void redrawBackground()
	{
//		reTile();
		drawBack();
	}
	
	public void drawAll()
	{
		drawBack();
//		drawMapMarksLayer();
		cleanLayers();
		drawAcLayers();		
	}
	
}
